package org.happy.tester.hitest.support.listener;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.happy.tester.common.http.CatcherClient;
import org.happy.tester.common.model.MockResultDO;
import org.happy.tester.common.model.PointcutDO;
import org.happy.tester.common.model.ResultDO;
import org.happy.tester.common.util.InternetUtils;
import org.happy.tester.hitest.support.annotation.Pointcut;
import org.testng.IMethodInstance;
import org.testng.IMethodInterceptor;
import org.testng.ITestContext;

import java.io.*;
import java.lang.reflect.Method;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.happy.tester.common.constant.CommonConstant.FILE_PATH_JOINER;

/**
 * @Classname HiTestMethodInterceptor
 * @Description 在所有测试类执行之前标记切点和修改切点返回值，和 HiTestSuiteListener 作用相同，只不过实现这个监听器更合适一些
 * @Date 2020/6/30 6:11 下午
 * @Created by jc.bai
 */
public class HiTestMethodInterceptor implements IMethodInterceptor {

    @Override
    public List<IMethodInstance> intercept(List<IMethodInstance> methods, ITestContext context) {
        Map<String, List<PointcutDO>> collectPointcutDOSMap = new HashMap<>();
        Map<String, List<MockResultDO>> mockResultDOMap = new HashMap<>();
        methods.stream().filter(iMethodInstance -> iMethodInstance.getMethod().getConstructorOrMethod().getMethod().isAnnotationPresent(Pointcut.class))
                .forEach(iMethodInstance -> {
                    Method method = iMethodInstance.getMethod().getConstructorOrMethod().getMethod();
                    Pointcut pointcut = method.getAnnotation(Pointcut.class);
                    PointcutDO pointcutDO = new PointcutDO();
                    switch (pointcut.type()) {
                        case COLLECT:
                            generatePointcutDOGroupMap(collectPointcutDOSMap, pointcutDO, pointcut);
                            break;
                        case MOCK:
                            generateMockResultMap(mockResultDOMap, pointcutDO, pointcut);
                            break;
                        default:
                            break;
                    }
                });
        try {
            if (!collectPointcutDOSMap.isEmpty()) {
                markCollect(collectPointcutDOSMap);
            }
            if (!mockResultDOMap.isEmpty()) {
                fixMockResult(mockResultDOMap);
            }
        } catch (UnknownHostException e) {
            System.out.println("error occur : UnknownHostException");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return methods;
    }

    private void markCollect(Map<String, List<PointcutDO>> collectPointcutDOSMap) throws IOException {
        JSONArray pointcutList = new JSONArray();
        Map<String, Object> pointcutListMap = new HashMap<>();
        for (Map.Entry<String, List<PointcutDO>> pointcutDOGroupMap : collectPointcutDOSMap.entrySet()) {
            pointcutListMap.put("appIp", pointcutDOGroupMap.getKey());
            pointcutListMap.put("pointcutDOS", pointcutDOGroupMap.getValue());
            pointcutListMap.put("clientIp", InternetUtils.getOutSideIp());
            System.out.println("clientIp" + InternetUtils.getOutSideIp());
            pointcutList.add(pointcutListMap);
        }
        CatcherClient catcherClient = new CatcherClient();
        catcherClient.markCollect(pointcutList);
    }

    private void fixMockResult(Map<String, List<MockResultDO>> mockResultDOMap) throws IOException {
        JSONArray mockResultList = new JSONArray();
        Map<String, Object> mockResultListMap = new HashMap<>();
        for (Map.Entry<String, List<MockResultDO>> mockResult : mockResultDOMap.entrySet()) {
            mockResultListMap.put("appIp", mockResult.getKey());
            mockResultListMap.put("mockResultDOList", mockResult.getValue());
            mockResultListMap.put("clientIp", InternetUtils.getOutSideIp());
            mockResultList.add(mockResultListMap);
        }
        CatcherClient catcherClient = new CatcherClient();
        catcherClient.fixMockResult(mockResultList);
    }

    private void generatePointcutDOGroupMap(Map<String, List<PointcutDO>> collectPointcutDOSMap, PointcutDO pointcutDO, Pointcut pointcut) {
        pointcutDO.setClassName(pointcut.className());
        pointcutDO.setMethodName(pointcut.methodName());
        pointcutDO.setParamsTypeArray(pointcut.paramsTypeArray());
        if (!collectPointcutDOSMap.containsKey(pointcut.appIp())) {
            List<PointcutDO> pointcutDOList = new ArrayList<>();
            pointcutDOList.add(pointcutDO);
            collectPointcutDOSMap.put(pointcut.appIp(), pointcutDOList);
            return;
        }
        collectPointcutDOSMap.get(pointcut.appIp()).add(pointcutDO);
    }

    private void generateMockResultMap(Map<String, List<MockResultDO>> mockResultDOMap, PointcutDO pointcutDO, Pointcut pointcut) {
        pointcutDO.setClassName(pointcut.className());
        pointcutDO.setMethodName(pointcut.methodName());
        pointcutDO.setParamsTypeArray(pointcut.paramsTypeArray());
        String result = getMockResult(pointcut);
        if (StringUtils.isEmpty(result)) {
            return;
        }
        ResultDO resultDO = JSON.parseObject(result, ResultDO.class);
        MockResultDO mockResultDO = new MockResultDO();
        mockResultDO.setPointcutDO(pointcutDO);
        mockResultDO.setResultDO(resultDO);
        if (!mockResultDOMap.containsKey(pointcut.appIp())) {
            List<MockResultDO> mockResultDOS = new ArrayList<>();
            mockResultDOS.add(mockResultDO);
            mockResultDOMap.put(pointcut.appIp(), mockResultDOS);
            return;
        }
        mockResultDOMap.get(pointcut.appIp()).add(mockResultDO);
    }

    /**
     * 先从数据库找，数据库中没有取注解中的value，value中没有从文件中读
     *
     * @return
     */
    private String getMockResult(Pointcut pointcut) {
        //getFromDB
        if (StringUtils.isEmpty(pointcut.value())) {
            String packagePath = pointcut.className().replaceAll("\\.", File.separator);
            String relativelyPath = FILE_PATH_JOINER.join(packagePath, pointcut.methodName().concat(".json"));
            return readResultFromJsonFile(relativelyPath);
        } else {
            return pointcut.value();
        }
    }

    /**
     * 从本地文件中读取mockResult
     *
     * @param relativelyPath 包路径+类名.json
     * @return
     */
    private String readResultFromJsonFile(String relativelyPath) {
        String physicalPath = System.getProperty("user.dir");
        String filePath = FILE_PATH_JOINER.join(physicalPath, RESULT_LOCATION, relativelyPath);
        File file = new File(filePath);
        String result = "";
        if (!file.exists()) {
            return result;
        }
        InputStreamReader isr = null;
        BufferedReader br = null;
        try {
            isr = new InputStreamReader(new FileInputStream(file), "utf-8");
            br = new BufferedReader(isr);
            String next = "";
            while ((next = br.readLine()) != null) {
                result = result.concat(next.trim());
            }
            return result;
        } catch (FileNotFoundException | UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }

    public static final String RESULT_LOCATION = FILE_PATH_JOINER.join("src", "test", "resources", "json");

}
